﻿

#region Using Statements

// System References
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework.Graphics;
using Freeform.Framework.Utility;
using Freeform.Framework.Input;
using Game = Freeform.Framework.Game;

#endregion


namespace Freeform.Framework
{
    /// <summary>
    /// Basically every class in the engine extends from Element. Each Element carries a unique ID so that they can be referenced
    /// remotely via network messages or so that cyclic references can be maintained when a game is serialized to persistant storage.
    /// </summary>
    public class Element
    {

        #region Events

        /// <summary>
        /// This event is fired before any update routines are done.
        /// </summary>
        public event EventHandler UpdateBeginning;


        /// <summary>
        /// This event is fired when the entity has finished updating.
        /// </summary>
        public event EventHandler UpdateEnded;

        #endregion


        #region Members

        /// <summary>
        /// Gets the ID of this Element.
        /// </summary>
        public UInt64 ID { get; private set; }


        /// <summary>
        /// Gets the next ID to be issued.
        /// </summary>
        internal static UInt64 NextID { get; set; }


        /// <summary>
        /// Gets a reference to the game that this Element belongs to.
        /// </summary>
        public Game Game { get; private set; }


        /// <summary>
        /// Gets whether or not this entity has been disposed.
        /// </summary>
        public Boolean IsDisposed { get; private set; }


        /// <summary>
        /// Gets or sets whether or not the Element is currently loaded.
        /// </summary>
        public Boolean IsLoaded { get; set; }


        /// <summary>
        /// Gets or sets whether this entity is currently enabled (receives updates.)
        /// </summary>
        public Boolean IsEnabled { get; set; }


        /// <summary>
        /// Gets the UpdateCode which is used to determine if an Element is up to date.
        /// </summary>
        public UInt64 UpdateCode { get; private set; }


        /// <summary>
        /// Gets whether or not the Entity is at the most recent update version.
        /// </summary>
        public Boolean IsUpToDate { get { return UpdateCode == Game.UpdateCode; } }


        /// <summary>
        /// Gets or sets the Element that this Element updates after if update order is required.
        /// </summary>
        public Element UpdatesAfter { get; set; }

        #region Private Members

        #endregion

        #endregion


        #region Init

        /// <summary>
        /// Creates a new Element for the game.
        /// </summary>
        /// <param name="game">The game.</param>
        public Element()
        {
            Game = Game.GameRef;
            ID = NextID++;
            IsEnabled = true;
            IsLoaded = true;
            Game.RegisterElement(this);
        }

        #endregion


        #region Update

        /// <summary>
        /// Allows the Element to update itself.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        public void Update(GameTime Time)
        {
            UpdateCode = Game.UpdateCode;
            if (UpdatesAfter != null && !UpdatesAfter.IsUpToDate)
                UpdatesAfter.Update(Time);

            if (UpdateBeginning != null) UpdateBeginning(this, new EventArgs());

            BeginUpdate(Time);
            DoUpdate(Time);
            EndUpdate(Time);

            if (UpdateEnded != null) UpdateEnded(this, new EventArgs());
        }


        protected virtual void BeginUpdate(GameTime Time) { }
        protected virtual void DoUpdate(GameTime Time) { }
        protected virtual void EndUpdate(GameTime Time) { }


        #endregion


        #region Dispose

        /// <summary>
        /// Disposes the entity and removes it from the world.
        /// </summary>
        public void Dispose()
        {
            Game.UnregisterElement(this);
            IsDisposed = true;

            if (!IsDisposed)
                OnDispose();
        }


        /// <summary>
        /// Actions to carry out upon disposal.
        /// </summary>
        protected virtual void OnDispose() 
        {
            
        }

        #endregion


        #region Static

        /// <summary>
        /// Attempts to lookup a Element.
        /// </summary>
        /// <param name="ID">The ID of the Element.</param>
        /// <returns>An Element.</returns>
        public static Element Lookup(UInt64 ID) { return Game.GameRef.GetElement(ID); }

        #endregion

    }
}
